export enum TailPosition {
  None = 0,
  BottomRight = 1,
  BottomLeft = 2,
  BottomMiddle = 3,
}

export interface TooltipOptions {
  contentLeft: number;
  contentTop: number;
  contentWidth: number;
  contentHeight: number;
  contentMargin: number;
  contentPadding: number;
  borderRadius: number;
  primaryColor: string;
  backgroundColor?: string;
  borderWidth: number;
  borderLength: number;
  tailLine1Rotate: number;
  tailLine1Length: number;
  tailLine2Length: number;
  tailPostion: TailPosition;
}

export function renderRect(
  ctx: CanvasRenderingContext2D,
  x,
  y,
  width,
  height,
  borderRadius,
  borderWidth
) {
  ctx.beginPath();
  y += borderWidth / 2;
  x += borderWidth / 2;
  ctx.moveTo(x + borderWidth + borderRadius, y);
  ctx.lineTo(x + width - borderWidth - borderRadius, y);
  ctx.arcTo(
    x + width - borderWidth,
    y,
    x + width - borderWidth,
    y + borderRadius,
    borderRadius
  );
  ctx.lineTo(x + width - borderWidth, y + height - borderWidth - borderRadius);
  ctx.arcTo(
    x + width - borderWidth,
    y + height - borderWidth,
    x + width - borderWidth - borderRadius,
    y + height - borderWidth,
    borderRadius
  );

  ctx.lineTo(x + borderWidth + borderRadius, y + height - borderWidth);
  ctx.arcTo(
    x,
    y + height - borderWidth,
    x,
    y + height - borderWidth - borderRadius,
    borderRadius
  );
  ctx.lineTo(x, y + borderWidth + borderRadius);
  ctx.arcTo(x, y, x + borderWidth + borderRadius, y, borderRadius);

  ctx.closePath();
  if (borderWidth > 0) {
    ctx.lineWidth = borderWidth;
    ctx.stroke();
  }

  ctx.fill();
}

export function renderTooltip(
  ctx: CanvasRenderingContext2D,
  options: TooltipOptions
) {
  const {
    contentLeft,
    contentTop,
    contentWidth,
    contentHeight,
    contentMargin,
    borderRadius,
    primaryColor,
    backgroundColor,
    borderWidth,
    borderLength,
    tailLine1Rotate,
    tailLine1Length,
    tailLine2Length,
    tailPostion,
  } = options;
  ctx.strokeStyle = primaryColor;
  if (backgroundColor) {
    ctx.fillStyle = backgroundColor;
  } else {
    ctx.fillStyle = ctx.strokeStyle + "22";
  }

  renderRect(
    ctx,
    contentLeft + contentMargin,
    contentTop + contentMargin,
    contentWidth - contentMargin * 2,
    contentHeight - contentMargin * 2,
    borderRadius,
    0
  );
  if (borderWidth > 0) {
    renderBottomLeft(
      ctx,
      contentLeft,
      contentTop,
      contentWidth,
      contentHeight,
      borderLength,
      borderWidth,
      primaryColor
    );
    renderBottomRight(
      ctx,
      contentLeft,
      contentTop,
      contentWidth,
      contentHeight,
      borderLength,
      borderWidth,
      primaryColor
    );
    renderTopLeft(
      ctx,
      contentLeft,
      contentTop,
      contentWidth,
      contentHeight,
      borderLength,
      borderWidth,
      primaryColor
    );
    renderTopRight(
      ctx,
      contentLeft,
      contentTop,
      contentWidth,
      contentHeight,
      borderLength,
      borderWidth,
      primaryColor
    );
  }

  if (tailPostion == TailPosition.BottomLeft) {
    renderBottomLeftTail(
      ctx,
      contentLeft,
      contentTop,
      contentWidth,
      contentHeight,
      tailLine1Rotate,
      tailLine1Length,
      tailLine2Length,
      borderWidth,
      primaryColor
    );
  } else if (tailPostion == TailPosition.BottomRight) {
    renderBottomRightTail(
      ctx,
      contentLeft,
      contentTop,
      contentWidth,
      contentHeight,
      tailLine1Rotate,
      tailLine1Length,
      tailLine2Length,
      borderWidth,
      primaryColor
    );
  } else if (tailPostion == TailPosition.BottomMiddle) {
    renderBottomMiddleTail(
      ctx,
      contentLeft,
      contentTop,
      contentWidth,
      contentHeight,
      tailLine1Rotate,
      tailLine1Length,
      tailLine2Length,
      borderWidth,
      primaryColor
    );
  }
}

export function renderTopLeft(
  ctx: CanvasRenderingContext2D,
  x: number,
  y: number,
  width: number,
  height: number,
  length: number,
  borderWidth: number,
  color: string
) {
  const x1 = x;
  const y1 = y;
  let x2, y2;
  ctx.lineWidth = borderWidth;

  ctx.beginPath();
  ctx.moveTo(x1, y1 + borderWidth / 2);
  x2 = x1 + length;
  y2 = y1 + borderWidth / 2;
  ctx.lineTo(x2, y2);
  ctx.strokeStyle = getBorderStrokeStyle(ctx, x1, y1, x2, y2, color);
  ctx.stroke();

  ctx.beginPath();
  ctx.moveTo(x1 + borderWidth / 2, y1);
  x2 = x1 + borderWidth / 2;
  y2 = y1 + length;
  ctx.lineTo(x2, y2);
  ctx.strokeStyle = getBorderStrokeStyle(ctx, x1, y1, x2, y2, color);
  ctx.stroke();
}

export function renderTopRight(
  ctx: CanvasRenderingContext2D,
  x: number,
  y: number,
  width: number,
  height: number,
  length: number,
  borderWidth: number,
  color: string
) {
  const x1 = x + width;
  const y1 = y;
  let x2, y2;
  ctx.lineWidth = borderWidth;

  ctx.beginPath();
  ctx.moveTo(x1, y1 + borderWidth / 2);
  x2 = x1 - length;
  y2 = y1 + borderWidth / 2;
  ctx.lineTo(x2, y2);
  ctx.strokeStyle = getBorderStrokeStyle(ctx, x1, y1, x2, y2, color);
  ctx.stroke();

  ctx.beginPath();
  ctx.moveTo(x1 - borderWidth / 2, y1);
  x2 = x1 - borderWidth / 2;
  y2 = y1 + length;
  ctx.lineTo(x2, y2);
  ctx.strokeStyle = getBorderStrokeStyle(ctx, x1, y1, x2, y2, color);
  ctx.stroke();
}

export function renderBottomLeft(
  ctx: CanvasRenderingContext2D,
  x: number,
  y: number,
  width: number,
  height: number,
  length: number,
  borderWidth: number,
  color: string
) {
  const x1 = x;
  const y1 = y + height;
  let x2, y2;
  ctx.lineWidth = borderWidth;

  ctx.beginPath();
  ctx.moveTo(x1, y1 - borderWidth / 2);
  x2 = x1 + length;
  y2 = y1 - borderWidth / 2;
  ctx.lineTo(x2, y2);
  ctx.strokeStyle = getBorderStrokeStyle(ctx, x1, y1, x2, y2, color);
  ctx.stroke();

  ctx.beginPath();
  ctx.moveTo(x1 + borderWidth / 2, y1);
  x2 = x1 + borderWidth / 2;
  y2 = y1 - length + borderWidth / 2;
  ctx.lineTo(x2, y2);
  ctx.strokeStyle = getBorderStrokeStyle(ctx, x1, y1, x2, y2, color);
  ctx.stroke();
}

export function renderBottomRight(
  ctx: CanvasRenderingContext2D,
  x: number,
  y: number,
  width: number,
  height: number,
  length: number,
  borderWidth: number,
  color: string
) {
  const x1 = x + width;
  const y1 = y + height;
  let x2, y2;
  ctx.lineWidth = borderWidth;

  ctx.beginPath();
  ctx.moveTo(x1, y1 - borderWidth / 2);
  x2 = x1 - length;
  y2 = y1 - borderWidth / 2;
  ctx.lineTo(x2, y2);
  ctx.strokeStyle = getBorderStrokeStyle(ctx, x1, y1, x2, y2, color);
  ctx.stroke();

  ctx.beginPath();
  ctx.moveTo(x1 - borderWidth / 2, y1);
  x2 = x1 - borderWidth / 2;
  y2 = y1 - length;
  ctx.lineTo(x2, y2);
  ctx.strokeStyle = getBorderStrokeStyle(ctx, x1, y1, x2, y2, color);
  ctx.stroke();
}

export function renderBottomRightTail(
  ctx: CanvasRenderingContext2D,
  x: number,
  y: number,
  width: number,
  height: number,
  line1Rotate: number,
  line1Length: number,
  line2Length: number,
  borderWidth: number,
  color: string
) {
  const x1 = x + width;
  const y1 = y + height;
  ctx.beginPath();
  ctx.moveTo(x1 - borderWidth / 2, y1 - borderWidth / 2);
  const pi = Math.PI * ((90 - line1Rotate) / 180);
  const x2 = x1 + Math.sin(pi) * line1Length;
  const y2 = y1 + Math.cos(pi) * line1Length;
  ctx.lineTo(x2, y2);
  const x3 = x2 + line2Length;
  const y3 = y2;
  ctx.lineTo(x3, y3);

  ctx.lineWidth = borderWidth;
  ctx.strokeStyle = color;
  ctx.stroke();
  ctx.beginPath();
  ctx.arc(x3 + borderWidth * 2, y3, borderWidth * 2, 0, 360);
  ctx.stroke();
}

export function renderBottomMiddleTail(
  ctx: CanvasRenderingContext2D,
  x: number,
  y: number,
  width: number,
  height: number,
  line1Rotate: number,
  line1Length: number,
  line2Length: number,
  borderWidth: number,
  color: string
) {
  const x1 = x + width / 2;
  const y1 = y + height;
  const edgeWidth = 0.2;
  ctx.beginPath();
  const left = x1 - (edgeWidth / 2) * width;
  const right = x1 + (edgeWidth / 2) * width;
  ctx.moveTo(left, y1);
  ctx.lineTo(right, y1);
  ctx.lineWidth = borderWidth;
  ctx.strokeStyle = getBorderStrokeStyle2(ctx, left, y1, right, y1, color);
  ctx.stroke();

  ctx.beginPath();
  ctx.moveTo(x1, y1);
  const pi = Math.PI * ((90 - line1Rotate) / 180);
  const x2 = x1 + Math.sin(pi) * line1Length;
  const y2 = y1 + Math.cos(pi) * line1Length;
  ctx.lineTo(x2, y2);
  const x3 = x2;
  const y3 = y2 + line2Length;
  ctx.lineTo(x3, y3);

  ctx.lineWidth = borderWidth;
  ctx.strokeStyle = color;
  ctx.stroke();
  ctx.beginPath();
  ctx.arc(x3, y3 + borderWidth * 2, borderWidth * 2, 0, 360);
  ctx.stroke();
}

export function renderBottomLeftTail(
  ctx: CanvasRenderingContext2D,
  x: number,
  y: number,
  width: number,
  height: number,
  line1Rotate: number,
  line1Length: number,
  line2Length: number,
  borderWidth: number,
  color: string
) {
  const x1 = x;
  const y1 = y + height;
  ctx.beginPath();
  ctx.moveTo(x1 + borderWidth / 2, y1 - borderWidth / 2);
  const r = Math.PI * ((90 - line1Rotate) / 180);
  const x2 = x1 - Math.sin(r) * line1Length;
  const y2 = y1 + Math.cos(r) * line1Length;
  ctx.lineTo(x2, y2);
  const x3 = x2 - line2Length;
  const y3 = y2;
  ctx.lineTo(x3, y3);

  ctx.lineWidth = borderWidth;
  ctx.strokeStyle = color;
  ctx.stroke();
  ctx.beginPath();
  ctx.arc(x3 - borderWidth * 2, y3, borderWidth * 2, 0, 360);
  ctx.stroke();
}
export function getTailSize(
  line1Rotate: number,
  line1Length: number,
  line2Length: number,
  borderWidth: number,
  tailPosition: TailPosition
) {
  if (
    tailPosition == TailPosition.BottomLeft ||
    tailPosition == TailPosition.BottomRight
  ) {
    const r = Math.PI * ((90 - line1Rotate) / 180);
    const width = Math.sin(r) * line1Length + line2Length + borderWidth * 4.5;
    const height = Math.cos(r) * line1Length + borderWidth * 2.5;
    return { width, height };
  } else if (tailPosition == TailPosition.BottomMiddle) {
    const r = Math.PI * ((90 - line1Rotate) / 180);
    const width = Math.sin(r) * line1Length;
    const height = Math.cos(r) * line1Length + line2Length + borderWidth * 4.5;
    return { width, height };
  }

  return { with: 0, height: 0 };
}

function getBorderStrokeStyle(
  ctx: CanvasRenderingContext2D,
  x: number,
  y: number,
  x1: number,
  y1: number,
  color: string
) {
  var jb = ctx.createLinearGradient(x, y, x1, y1);
  jb.addColorStop(0, color);
  jb.addColorStop(0.1, color);
  jb.addColorStop(1, `${color}00`);
  return jb;
}

function getBorderStrokeStyle2(
  ctx: CanvasRenderingContext2D,
  x: number,
  y: number,
  x1: number,
  y1: number,
  color: string
) {
  var jb = ctx.createLinearGradient(x, y, x1, y1);
  jb.addColorStop(0, `${color}00`);
  jb.addColorStop(0.5, color);
  jb.addColorStop(1, `${color}00`);
  return jb;
}

export function getTooltipSizeInfo(options: TooltipOptions): TooltipSizeInfo {
  let {
    contentLeft,
    contentTop,
    contentWidth,
    contentHeight,
    contentMargin,
    contentPadding,
    borderWidth,
    tailLine1Rotate,
    tailLine1Length,
    tailLine2Length,
    tailPostion: tailPosition,
  } = options;
  const { width: tailWidth, height: tailHeight } = getTailSize(
    tailLine1Rotate,
    tailLine1Length,
    tailLine2Length,
    borderWidth,
    tailPosition
  );
  contentWidth += contentMargin * 2 + contentPadding * 2 + borderWidth * 2;
  contentHeight += contentMargin * 2 + contentPadding * 2 + borderWidth * 2;
  let canvasWidth: number;
  let canvasHeight: number;

  if (
    tailPosition == TailPosition.BottomLeft ||
    tailPosition == TailPosition.BottomRight
  ) {
    canvasWidth = contentLeft + tailWidth + contentWidth;
    canvasHeight = contentTop + tailHeight + contentHeight;
    if (tailPosition == TailPosition.BottomLeft) {
      contentLeft += tailWidth;
    }
  } else if (tailPosition == TailPosition.BottomMiddle) {
    const dw = contentWidth / 2 - tailWidth;
    console.log(
      "contentWidth",
      contentWidth,
      "tailWidth",
      tailWidth,
      "tailHeight",
      tailHeight
    );
    canvasWidth = contentLeft + contentWidth + (dw >= 0 ? 0 : -dw);
    canvasHeight = contentTop + tailHeight + contentHeight;
  } else {
    canvasWidth = contentLeft + contentWidth;
    canvasHeight = contentTop + contentHeight;
  }

  return {
    canvasWidth,
    canvasHeight,
    contentLeft,
    contentTop,
    contentWidth,
    contentHeight,
  };
}

export interface TooltipSizeInfo {
  canvasWidth: number;
  canvasHeight: number;
  contentLeft: number;
  contentTop: number;
  contentWidth: number;
  contentHeight: number;
}
